// REQUIRES: concurrency

func withError(_ completion: @escaping (String?, Error?) -> Void) { }
func notOptional(_ completion: @escaping (String, Error?) -> Void) { }
func errorOnly(_ completion: @escaping (Error?) -> Void) { }
func test(_ str: String) -> Bool { return false }

// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNRELATED %s
withError { res, err in
  if test("unrelated") {
    print("unrelated")
  } else {
    print("else unrelated")
  }
}
// UNRELATED: convert_params_single.swift
// UNRELATED-NEXT: let res = try await withError()
// UNRELATED-NEXT: if test("unrelated") {
// UNRELATED-NEXT: print("unrelated")
// UNRELATED-NEXT: } else {
// UNRELATED-NEXT: print("else unrelated")
// UNRELATED-NEXT: }

// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=BOUND %s
withError { res, err in
  print("before")
  if let bad = err {
    print("got error \(bad)")
    return
  }
  if let str = res {
    print("got result \(str)")
  }
  print("after")
}
// BOUND: do {
// BOUND-NEXT: let str = try await withError()
// BOUND-NEXT: print("before")
// BOUND-NEXT: print("got result \(str)")
// BOUND-NEXT: print("after")
// BOUND-NEXT: } catch let bad {
// BOUND-NEXT: print("got error \(bad)")
// BOUND-NEXT: }

// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=BOUND-COMMENT %s
withError { res, err in // a
  // b
  print("before")
  // c
  if let bad = err { // d
    // e
    print("got error \(bad)")
    // f
    return
    // g
  }
  // h
  if let str = res { // i
    // j
    print("got result \(str)")
    // k
  }
  // l
  print("after")
  // m
}
// BOUND-COMMENT: do {
// BOUND-COMMENT-NEXT: let str = try await withError()
// BOUND-COMMENT-NEXT: // a
// BOUND-COMMENT-NEXT: // b
// BOUND-COMMENT-NEXT: print("before")
// BOUND-COMMENT-NEXT: // c
// BOUND-COMMENT-NEXT: // h
// BOUND-COMMENT-NEXT: // i
// BOUND-COMMENT-NEXT: // j
// BOUND-COMMENT-NEXT: print("got result \(str)")
// BOUND-COMMENT-NEXT: // k
// BOUND-COMMENT-NEXT: // l
// BOUND-COMMENT-NEXT: print("after")
// BOUND-COMMENT-NEXT: // m
// BOUND-COMMENT-EMPTY:
// BOUND-COMMENT-NEXT: } catch let bad {
// BOUND-COMMENT-NEXT: // d
// BOUND-COMMENT-NEXT: // e
// BOUND-COMMENT-NEXT: print("got error \(bad)")
// BOUND-COMMENT-NEXT: // f
// BOUND-COMMENT-NEXT: // g
// BOUND-COMMENT-NEXT: {{ }}
// BOUND-COMMENT-NEXT: }


// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND-ERR %s
withError { res, err in
  print("before")
  guard let str = res else {
    print("got error \(err!)")
    return
  }
  print("got result \(str)")
  print("after")
}
// UNBOUND-ERR: do {
// UNBOUND-ERR-NEXT: let str = try await withError()
// UNBOUND-ERR-NEXT: print("before")
// UNBOUND-ERR-NEXT: print("got result \(str)")
// UNBOUND-ERR-NEXT: print("after")
// UNBOUND-ERR-NEXT: } catch let err {
// UNBOUND-ERR-NEXT: print("got error \(err)")
// UNBOUND-ERR-NEXT: }

// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=BOUND %s
withError { res, err in
  print("before")
  if let bad = err {
    print("got error \(bad)")
  } else if let str = res {
    print("got result \(str)")
  }
  print("after")
}

// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=BOUND %s
withError { res, err in
  print("before")
  if let bad = err {
    print("got error \(bad)")
    return
  }
  if let str = res {
    print("got result \(str)")
  }
  print("after")
}

// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND-RES %s
withError { res, err in
  print("before")
  if let bad = err {
    print("got error \(bad)")
    return
  }
  print("got result \(res!)")
  print("after")
}
// UNBOUND-RES: do {
// UNBOUND-RES-NEXT: let res = try await withError()
// UNBOUND-RES-NEXT: print("before")
// UNBOUND-RES-NEXT: print("got result \(res)")
// UNBOUND-RES-NEXT: print("after")
// UNBOUND-RES-NEXT: } catch let bad {
// UNBOUND-RES-NEXT: print("got error \(bad)")
// UNBOUND-RES-NEXT: }

// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND-ERR %s
withError { res, err in
  print("before")
  if let str = res {
    print("got result \(str)")
    print("after")
    return
  }
  print("got error \(err!)")
}

// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND-RES %s
withError { res, err in
  print("before")
  if let bad = err {
    print("got error \(bad)")
  } else {
    print("got result \(res!)")
  }
  print("after")
}

// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND-ERR %s
withError { res, err in
  print("before")
  if let str = res {
    print("got result \(str)")
  } else {
    print("got error \(err!)")
  }
  print("after")
}

// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND %s
withError { res, err in
  print("before")
  if err != nil {
    print("got error \(err!)")
    return
  }
  print("got result \(res!)")
  print("after")
}
// UNBOUND: do {
// UNBOUND-NEXT: let res = try await withError()
// UNBOUND-NEXT: print("before")
// UNBOUND-NEXT: print("got result \(res)")
// UNBOUND-NEXT: print("after")
// UNBOUND-NEXT: } catch let err {
// UNBOUND-NEXT: print("got error \(err)")
// UNBOUND-NEXT: }

// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND %s
withError { res, err in
  print("before")
  if res != nil {
    print("got result \(res!)")
    print("after")
    return
  }
  print("got error \(err!)")
}

// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND %s
withError { res, err in
  print("before")
  if err != nil {
    print("got error \(err!)")
  } else {
    print("got result \(res!)")
  }
  print("after")
}

// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND %s
withError { res, err in
  print("before")
  if res != nil {
    print("got result \(res!)")
  } else {
    print("got error \(err!)")
  }
  print("after")
}

// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND %s
withError { res, err in
  print("before")
  if err == nil {
    print("got result \(res!)")
  } else {
    print("got error \(err!)")
  }
  print("after")
}

// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND %s
withError { res, err in
  print("before")
  if res == nil {
    print("got error \(err!)")
  } else {
    print("got result \(res!)")
  }
  print("after")
}

// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNHANDLEDNESTED %s
withError { res, err in
  print("before")
  if let bad = err {
    print("got error \(bad)")
  } else {
    if let str = res {
      print("got result \(str)")
    }
  }
  print("after")
}
// UNHANDLEDNESTED: do {
// UNHANDLEDNESTED-NEXT: let res = try await withError()
// UNHANDLEDNESTED-NEXT: print("before")
// UNHANDLEDNESTED-NEXT: if let str = <#res#> {
// UNHANDLEDNESTED-NEXT: print("got result \(str)")
// UNHANDLEDNESTED-NEXT: }
// UNHANDLEDNESTED-NEXT: print("after")
// UNHANDLEDNESTED-NEXT: } catch let bad {
// UNHANDLEDNESTED-NEXT: print("got error \(bad)")
// UNHANDLEDNESTED-NEXT: }

// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=NOERR %s
withError { res, err in
  print("before")
  if let str = res {
    print("got result \(str)")
  }
  print("after")
}
// NOERR: convert_params_single.swift
// NOERR-NEXT: let str = try await withError()
// NOERR-NEXT: print("before")
// NOERR-NEXT: print("got result \(str)")
// NOERR-NEXT: print("after")
// NOERR-NOT: }

// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=NORES %s
withError { res, err in
  print("before")
  if let bad = err {
    print("got error \(bad)")
  }
  print("after")
}
// NORES: do {
// NORES-NEXT: let res = try await withError()
// NORES-NEXT: print("before")
// NORES-NEXT: print("after")
// NORES-NEXT: } catch let bad {
// NORES-NEXT: print("got error \(bad)")
// NORES-NEXT: }

// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND %s
withError { res, err in
  print("before")
  if ((res != (nil)) && err == nil) {
    print("got result \(res!)")
  } else {
    print("got error \(err!)")
  }
  print("after")
}

// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNKNOWN-COND %s
withError { res, err in
  print("before")
  if res != nil && test(res!) {
    print("got result \(res!)")
  }
  print("after")
}
// UNKNOWN-COND: convert_params_single.swift
// UNKNOWN-COND-NEXT: let res = try await withError()
// UNKNOWN-COND-NEXT: print("before")
// UNKNOWN-COND-NEXT: if <#res#> != nil && test(res) {
// UNKNOWN-COND-NEXT: print("got result \(res)")
// UNKNOWN-COND-NEXT: }
// UNKNOWN-COND-NEXT: print("after")

// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNKNOWN-CONDELSE %s
withError { res, err in
  print("before")
  if res != nil && test(res!) {
    print("got result \(res!)")
  } else {
    print("bad")
  }
  print("after")
}
// UNKNOWN-CONDELSE: var res: String? = nil
// UNKNOWN-CONDELSE-NEXT: var err: Error? = nil
// UNKNOWN-CONDELSE-NEXT: do {
// UNKNOWN-CONDELSE-NEXT: res = try await withError()
// UNKNOWN-CONDELSE-NEXT: } catch {
// UNKNOWN-CONDELSE-NEXT: err = error
// UNKNOWN-CONDELSE-NEXT: }
// UNKNOWN-CONDELSE-EMPTY:
// UNKNOWN-CONDELSE-NEXT: print("before")
// UNKNOWN-CONDELSE-NEXT: if res != nil && test(res!) {
// UNKNOWN-CONDELSE-NEXT: print("got result \(res!)")
// UNKNOWN-CONDELSE-NEXT: } else {
// UNKNOWN-CONDELSE-NEXT: print("bad")
// UNKNOWN-CONDELSE-NEXT: }
// UNKNOWN-CONDELSE-NEXT: print("after")

// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MULTIBIND %s
withError { res, err in
  print("before")
  if let str = res {
    print("got result \(str)")
  }
  if let str2 = res {
    print("got result \(str2)")
  }
  if case (let str3?) = (res) {
    print("got result \(str3)")
  }
  print("after")
}
// MULTIBIND: let str = try await withError()
// MULTIBIND-NEXT: print("before")
// MULTIBIND-NEXT: print("got result \(str)")
// MULTIBIND-NEXT: print("got result \(str)")
// MULTIBIND-NEXT: print("got result \(str)")
// MULTIBIND-NEXT: print("after")

// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=NESTEDRET %s
withError { res, err in
  print("before")
  if let str = res {
    if test(str) {
      return
    }
    print("got result \(str)")
  }
  print("after")
}
// NESTEDRET: convert_params_single.swift
// NESTEDRET-NEXT: let str = try await withError()
// NESTEDRET-NEXT: print("before")
// NESTEDRET-NEXT: if test(str) {
// NESTEDRET-NEXT:   <#return#>
// NESTEDRET-NEXT: }
// NESTEDRET-NEXT: print("got result \(str)")
// NESTEDRET-NEXT: print("after")
// NESTEDRET-NOT: }

// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND-ERR %s
withError { res, err in
  print("before")
  guard let str = res, err == nil else {
    print("got error \(err!)")
    return
  }
  print("got result \(str)")
  print("after")
}

// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND %s
withError { res, err in
  print("before")
  guard res != nil else {
    print("got error \(err!)")
    return
  }
  print("got result \(res!)")
  print("after")
}

// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND %s
withError { res, err in
  print("before")
  guard err == nil else {
    print("got error \(err!)")
    return
  }
  print("got result \(res!)")
  print("after")
}

// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNBOUND %s
withError { res, err in
  print("before")
  guard res != nil && err == nil else {
    print("got error \(err!)")
    return
  }
  print("got result \(res!)")
  print("after")
}

// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=UNWRAPPING %s
withError { str, err in
  print("before")
  guard err == nil else { return }
  _ = str!.count
  _ = /*before*/str!/*after*/.count
  _ = str?.count
  _ = str!.count.bitWidth
  _ = str?.count.bitWidth
  _ = (str?.count.bitWidth)!
  _ = str!.first?.isWhitespace
  _ = str?.first?.isWhitespace
  _ = (str?.first?.isWhitespace)!
  print("after")
}
// UNWRAPPING:      let str = try await withError()
// UNWRAPPING-NEXT: print("before")
// UNWRAPPING-NEXT: _ = str.count
// UNWRAPPING-NEXT: _ = /*before*/str/*after*/.count
// UNWRAPPING-NEXT: _ = str.count
// UNWRAPPING-NEXT: _ = str.count.bitWidth
// UNWRAPPING-NEXT: _ = str.count.bitWidth

// Note this transform results in invalid code as str.count.bitWidth is no
// longer optional, but arguably it's more useful than leaving str as a
// placeholder. In general, the tranform we perform here is locally valid
// within the optional chain, but may change the type of the overall chain.
// UNWRAPPING-NEXT: _ = (str.count.bitWidth)!

// UNWRAPPING-NEXT: _ = str.first?.isWhitespace
// UNWRAPPING-NEXT: _ = str.first?.isWhitespace
// UNWRAPPING-NEXT: _ = (str.first?.isWhitespace)!
// UNWRAPPING-NEXT: print("after")

// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=NOT-OPTIONAL %s
notOptional { str, err in
  print("before")
  if let err2 = err {
    print("got error \(err2)")
    return
  }
  print("got result \(str)")
  print("after")
}
// NOT-OPTIONAL: do {
// NOT-OPTIONAL-NEXT: let str = try await notOptional()
// NOT-OPTIONAL-NEXT: print("before")
// NOT-OPTIONAL-NEXT: print("got result \(str)")
// NOT-OPTIONAL-NEXT: print("after")
// NOT-OPTIONAL-NEXT: } catch let err2 {
// NOT-OPTIONAL-NEXT: print("got error \(err2)")
// NOT-OPTIONAL-NEXT: }

// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=ERROR-ONLY %s
errorOnly { err in
  print("before")
  if let err2 = err {
    print("got error \(err2)")
    return
  }
  print("after")
}
// ERROR-ONLY: convert_params_single.swift
// ERROR-ONLY-NEXT: do {
// ERROR-ONLY-NEXT: try await errorOnly()
// ERROR-ONLY-NEXT: print("before")
// ERROR-ONLY-NEXT: print("after")
// ERROR-ONLY-NEXT: } catch let err2 {
// ERROR-ONLY-NEXT: print("got error \(err2)")
// ERROR-ONLY-NEXT: }
// ERROR-ONLY-NOT: }
